Guida Completa a Scapy: Ether, ARP, hexdump e srp

Introduzione a Scapy

Scapy è una potentissima libreria Python che permette di manipolare pacchetti di rete a tutti i livelli dello stack protocollare. A differenza di altri strumenti di analisi di rete, Scapy non si limita a catturare e visualizzare pacchetti, ma consente di costruire, modificare, inviare e ricevere pacchetti personalizzati con un controllo granulare su ogni singolo campo.

Questa caratteristica rende Scapy uno strumento fondamentale per:

Scapy opera principalmente in modalità interattiva (tramite shell Python) ma può essere utilizzato anche all’interno di script Python complessi. La sua filosofia di design si basa sulla semplicità d’uso combinata con una flessibilità estrema: con poche righe di codice è possibile creare pacchetti che richiederebbero centinaia di righe usando altre librerie.

Installazione di Scapy

Prima di procedere con l’analisi delle funzioni specifiche, è importante installare correttamente Scapy:

pip install scapy

Su sistemi Linux potrebbe essere necessario eseguire gli script con privilegi di amministratore (sudo) per accedere alle interfacce di rete a basso livello.


1. Ether: Il Livello Data Link

Cos’è Ether in Scapy?

Ether è la classe di Scapy che rappresenta un frame Ethernet, ovvero l’unità di dati fondamentale del livello 2 (Data Link) del modello OSI. Quando parliamo di comunicazione in reti locali (LAN), parliamo essenzialmente di frame Ethernet che viaggiano da un dispositivo all’altro attraverso switch e bridge.

Struttura di un Frame Ethernet

Un frame Ethernet è composto da diversi campi fondamentali:

  1. Indirizzo MAC di destinazione (dst): 6 byte che identificano univocamente la scheda di rete del destinatario
  2. Indirizzo MAC sorgente (src): 6 byte che identificano univocamente la scheda di rete del mittente
  3. Tipo/Lunghezza (type): 2 byte che indicano il protocollo del livello superiore (es. 0x0800 per IPv4, 0x0806 per ARP)
  4. Payload: i dati effettivi trasportati dal frame
  5. FCS (Frame Check Sequence): 4 byte di controllo errori (gestito automaticamente dall’hardware)

Utilizzo di Ether in Scapy

Quando importiamo Ether da Scapy, otteniamo accesso a una classe che ci permette di creare e manipolare frame Ethernet con estrema facilità. Vediamo alcuni esempi pratici:

from scapy.all import Ether

# Creare un frame Ethernet basilare
frame = Ether()

Quando creiamo un oggetto Ether() senza parametri, Scapy imposta automaticamente alcuni valori di default. Possiamo visualizzare questi valori:

frame.show()

Output tipico:

###[ Ethernet ]### 
  dst       = ff:ff:ff:ff:ff:ff  (broadcast)
  src       = 00:00:00:00:00:00
  type      = 0x9000

Come possiamo osservare, il campo dst viene impostato automaticamente su ff:ff:ff:ff:ff:ff, che è l’indirizzo MAC broadcast (tutti i dispositivi della rete locale riceveranno questo frame).

Personalizzazione dei Campi Ethernet

Possiamo specificare manualmente i campi del frame Ethernet:

frame_custom = Ether(dst="aa:bb:cc:dd:ee:ff", src="11:22:33:44:55:66")
frame_custom.show()

Questo è particolarmente utile quando vogliamo:

Incapsulamento di Protocolli Superiori

La vera potenza di Ether emerge quando lo combiniamo con protocolli di livello superiore. Scapy utilizza l’operatore / per “impilare” i livelli protocollari:

from scapy.all import Ether, IP

# Creare un frame Ethernet che contiene un pacchetto IP
packet = Ether()/IP(dst="192.168.1.1")
packet.show()

In questo esempio, stiamo creando un frame Ethernet che incapsula un pacchetto IP. Scapy imposterà automaticamente il campo type di Ethernet a 0x0800 (IPv4) perché ha rilevato che stiamo trasportando un pacchetto IP.

Comprensione del Campo Type

Il campo type in Ethernet è cruciale perché dice al ricevente quale protocollo di livello superiore è contenuto nel payload. I valori più comuni sono:

Scapy gestisce automaticamente questo campo quando impiliamo i protocolli, ma possiamo anche impostarlo manualmente se necessario per scopi di testing.

Applicazioni Pratiche di Ether

  1. Analisi del traffico locale: Catturare e analizzare frame Ethernet per comprendere chi comunica con chi nella LAN
  2. MAC Spoofing: Modificare l’indirizzo MAC sorgente per scopi di testing (attenzione alle implicazioni etiche e legali)
  3. Testing di switch: Verificare come gli switch gestiscono il forwarding basato su indirizzi MAC
  4. Troubleshooting: Identificare problemi di comunicazione a livello Data Link

2. ARP: Address Resolution Protocol

Introduzione ad ARP

ARP (Address Resolution Protocol) è uno dei protocolli fondamentali per il funzionamento delle reti locali. Il suo compito è risolvere il problema cruciale della traduzione tra indirizzi IP (livello 3, Network) e indirizzi MAC (livello 2, Data Link).

Il Problema che ARP Risolve

Immaginiamo questa situazione: il tuo computer (192.168.1.10) vuole inviare dati a un altro computer nella stessa rete locale (192.168.1.20). Il protocollo IP sa dove inviare i dati (all’indirizzo IP 192.168.1.20), ma a livello fisico, il frame Ethernet necessita dell’indirizzo MAC del destinatario. Come ottiene questo indirizzo MAC?

La risposta è: attraverso ARP!

Funzionamento di ARP: Request e Reply

Il processo ARP funziona in due fasi:

ARP Request (Richiesta broadcast):

Computer A: "Chi ha l'indirizzo IP 192.168.1.20? Per favore, rispondi con il tuo MAC address!"

Questa richiesta viene inviata in broadcast (a tutti i dispositivi della rete locale).

ARP Reply (Risposta unicast):

Computer B: "Io ho l'indirizzo IP 192.168.1.20 e il mio MAC address è aa:bb:cc:dd:ee:ff"

Solo il computer che possiede quell’indirizzo IP risponderà, inviando la sua risposta direttamente al richiedente.

Struttura di un Pacchetto ARP

Un pacchetto ARP contiene numerosi campi:

  1. hwtype (Hardware Type): tipo di tecnologia di rete (1 = Ethernet)
  2. ptype (Protocol Type): tipo di protocollo di livello 3 (0x0800 = IPv4)
  3. hwlen (Hardware Address Length): lunghezza indirizzo MAC (6 byte per Ethernet)
  4. plen (Protocol Address Length): lunghezza indirizzo IP (4 byte per IPv4)
  5. op (Operation): tipo di operazione (1 = request, 2 = reply)
  6. hwsrc (Hardware Source): MAC address del mittente
  7. psrc (Protocol Source): indirizzo IP del mittente
  8. hwdst (Hardware Destination): MAC address del destinatario
  9. pdst (Protocol Destination): indirizzo IP del destinatario

Utilizzo di ARP in Scapy

Vediamo come creare pacchetti ARP con Scapy:

from scapy.all import ARP

# Creare una ARP request basilare
arp_request = ARP()
arp_request.show()

Output tipico:

###[ ARP ]### 
  hwtype    = 0x1        (Ethernet)
  ptype     = 0x800      (IPv4)
  hwlen     = 6
  plen      = 4
  op        = who-has    (ARP request)
  hwsrc     = 00:00:00:00:00:00
  psrc      = 0.0.0.0
  hwdst     = 00:00:00:00:00:00
  pdst      = 0.0.0.0

Creare una ARP Request Completa

Per creare una richiesta ARP funzionante per scoprire il MAC address di un dispositivo:

from scapy.all import Ether, ARP

# Definiamo l'IP target di cui vogliamo scoprire il MAC
target_ip = "192.168.1.1"

# Creiamo il frame Ethernet (broadcast)
ether = Ether(dst="ff:ff:ff:ff:ff:ff")

# Creiamo la ARP request
arp = ARP(pdst=target_ip)

# Combiniamo i due livelli
packet = ether/arp
packet.show()

Questo pacchetto, quando inviato, chiederà: “Chi ha l’indirizzo IP 192.168.1.1? Rispondi al mio MAC address con il tuo!”

Creare una ARP Reply

Possiamo anche creare manualmente una risposta ARP (utile per testing o per comprendere attacchi come ARP poisoning):

arp_reply = ARP(
    op=2,                               # 2 = is-at (ARP reply)
    hwsrc="aa:bb:cc:dd:ee:ff",         # Il nostro MAC
    psrc="192.168.1.100",              # Il nostro IP
    hwdst="11:22:33:44:55:66",         # MAC del richiedente
    pdst="192.168.1.50"                # IP del richiedente
)

ARP Cache e Persistenza

I sistemi operativi mantengono una ARP cache (tabella ARP) che memorizza le associazioni IP-MAC apprese. Questo evita di dover inviare una ARP request ogni volta che si comunica con lo stesso host.

Su Linux puoi visualizzare la cache ARP con:

arp -a

Su Windows:

arp -a

Applicazioni Pratiche di ARP in Scapy

  1. Network Discovery: Scoprire tutti i dispositivi attivi in una rete locale
  2. MAC Address Verification: Verificare quale dispositivo sta usando un determinato IP
  3. Testing di sicurezza: Comprendere e testare vulnerabilità come ARP spoofing/poisoning
  4. Troubleshooting: Diagnosticare problemi di risoluzione IP-MAC

Esempio Avanzato: Scansione di Rete

from scapy.all import Ether, ARP, srp

# Definiamo il range di IP da scansionare
target_range = "192.168.1.0/24"

# Creiamo il pacchetto ARP per l'intera subnet
arp = ARP(pdst=target_range)
ether = Ether(dst="ff:ff:ff:ff:ff:ff")
packet = ether/arp

# Inviamo e riceviamo (vedremo srp più avanti)
result = srp(packet, timeout=2, verbose=0)[0]

# Elaboriamo i risultati
for sent, received in result:
    print(f"IP: {received.psrc} - MAC: {received.hwsrc}")

Questo script scansionerà tutti i 254 possibili indirizzi IP della subnet 192.168.1.0/24 e stamperà quali rispondono con il loro MAC address.


3. hexdump: Visualizzazione Esadecimale

Cos’è hexdump in Scapy?

hexdump è una funzione di utilità di Scapy che permette di visualizzare il contenuto grezzo di un pacchetto in formato esadecimale e ASCII. Questa rappresentazione è fondamentale per comprendere cosa realmente viene trasmesso “sulla rete” a livello di byte.

Perché la Visualizzazione Esadecimale è Importante?

Quando lavoriamo con protocolli di rete, è essenziale comprendere che tutti i dati viaggiano come sequenze di byte. La rappresentazione esadecimale ci permette di:

  1. Vedere i dati grezzi: Osservare esattamente quali byte compongono un pacchetto
  2. Verificare campi specifici: Controllare i valori esatti dei campi di un protocollo
  3. Debugging: Identificare problemi di encoding, padding o corruzione dei dati
  4. Apprendimento: Capire come i protocolli sono strutturati a livello binario
  5. Analisi forense: Esaminare pacchetti sospetti o malformati

Formato della Visualizzazione hexdump

La funzione hexdump produce un output strutturato in questo modo:

0000  ff ff ff ff ff ff aa bb  cc dd ee ff 08 06 00 01   ................
0010  08 00 06 04 00 01 aa bb  cc dd ee ff c0 a8 01 0a   ................

Analizziamo le tre sezioni:

  1. Offset (prima colonna): Indica la posizione in byte dall’inizio del pacchetto (in esadecimale)
  2. Dati esadecimali (colonne centrali): I byte effettivi rappresentati in formato esadecimale
  3. Rappresentazione ASCII (ultima colonna): Tentativo di visualizzare i byte come caratteri ASCII (i caratteri non stampabili sono mostrati come .)

Utilizzo Base di hexdump

from scapy.all import Ether, ARP, hexdump

# Creiamo un pacchetto
packet = Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="192.168.1.1")

# Visualizziamo in formato esadecimale
hexdump(packet)

Output esempio:

0000  FF FF FF FF FF FF 00 00  00 00 00 00 08 06 00 01   ................
0010  08 00 06 04 00 01 00 00  00 00 00 00 00 00 00 00   ................
0020  00 00 00 00 00 00 C0 A8  01 01                     ..........

Interpretazione dell’Output

Analizziamo il primo output byte per byte:

Offset 0000 - Header Ethernet:

Offset 0010 - Header ARP:

Offset 0020 - Continua ARP:

Conversione da Decimale a Esadecimale

Per comprendere meglio hexdump, è utile sapere come convertire tra rappresentazioni:

Quindi l’indirizzo IP 192.168.1.1 diventa C0 A8 01 01 in esadecimale.

hexdump vs show()

È importante distinguere tra hexdump() e show():

# show() fornisce una vista "interpretata" del pacchetto
packet.show()
# Output:
# ###[ Ethernet ]###
#   dst       = ff:ff:ff:ff:ff:ff
#   ...

# hexdump() mostra i byte grezzi
hexdump(packet)
# Output:
# 0000  FF FF FF FF FF FF ...

show() è utile per comprendere rapidamente la struttura logica del pacchetto, mentre hexdump() è essenziale per analisi approfondite e debugging a basso livello.

Applicazioni Pratiche di hexdump

  1. Verifica di conformità protocollare: Assicurarsi che i pacchetti rispettino le specifiche RFC
  2. Analisi di pacchetti catturati: Esaminare traffico reale per comprenderne il contenuto
  3. Debugging di implementazioni custom: Verificare che i pacchetti creati manualmente siano corretti
  4. Didattica: Insegnare agli studenti come i protocolli funzionano a livello binario
  5. Sicurezza: Identificare payload sospetti o tentativi di exploit

Esempio Avanzato: Analisi di un Pacchetto Completo

from scapy.all import Ether, ARP, IP, TCP, hexdump

# Creiamo un pacchetto complesso
complex_packet = Ether()/IP(dst="192.168.1.100")/TCP(dport=80, sport=12345)

print("=== Vista interpretata ===")
complex_packet.show()

print("\n=== Vista esadecimale ===")
hexdump(complex_packet)

Questo esempio mostra come hexdump sia particolarmente utile con pacchetti complessi multi-layer, permettendo di vedere esattamente come ogni livello protocollare è codificato in byte.

hexdump con Pacchetti Catturati

from scapy.all import sniff, hexdump

# Catturiamo un pacchetto dalla rete
packets = sniff(count=1)

# Visualizziamo in esadecimale
hexdump(packets[0])

Questo ci permette di analizzare il traffico reale della rete, vedendo esattamente cosa transita “sui fili”.


4. srp: Send and Receive Packets (Layer 2)

Introduzione a srp

srp (Send and Receive Packets) è una delle funzioni più potenti di Scapy. Permette di inviare pacchetti a livello 2 (Data Link) e ricevere le risposte, tutto in un’unica operazione. La “p” finale sta per “packets” al plurale, perché srp può inviare e gestire molteplici pacchetti contemporaneamente.

Differenza tra sr, srp e sr1

Scapy offre diverse varianti della funzione send/receive:

  1. sr(): Send/Receive a livello 3 (Network layer) - usa routing IP
  2. srp(): Send/Receive a livello 2 (Data Link layer) - usa indirizzamento MAC
  3. sr1(): Send/Receive con ritorno di un singolo pacchetto (livello 3)
  4. srp1(): Send/Receive con ritorno di un singolo pacchetto (livello 2)

srp è particolarmente importante quando lavoriamo con protocolli di livello 2 come ARP o quando vogliamo controllare direttamente gli indirizzi MAC.

Struttura Base di srp

from scapy.all import srp

answered, unanswered = srp(packet, timeout=2, verbose=True)

Vediamo i componenti:

Valore di Ritorno di srp

srp restituisce una tupla di due elementi:

(answered, unanswered)

answered è un oggetto SRPResult contenente coppie di pacchetti:

for sent, received in answered:
    print(f"Inviato: {sent.summary()}")
    print(f"Ricevuto: {received.summary()}")

unanswered è un PacketList contenente i pacchetti senza risposta:

for packet in unanswered:
    print(f"Nessuna risposta per: {packet.summary()}")

Esempio Completo: ARP Scan con srp

Vediamo un esempio pratico di network discovery utilizzando srp:

from scapy.all import Ether, ARP, srp

# Definiamo il range di IP da scansionare
target_ip = "192.168.1.0/24"

# Creiamo il pacchetto ARP
arp = ARP(pdst=target_ip)
ether = Ether(dst="ff:ff:ff:ff:ff:ff")
packet = ether/arp

# Inviamo i pacchetti e attendiamo le risposte
print("Scansione della rete in corso...")
answered, unanswered = srp(packet, timeout=2, verbose=0)

# Elaboriamo i risultati
print(f"\nDispositivi trovati: {len(answered)}\n")
print("IP\t\t\tMAC Address")
print("-" * 50)

for sent, received in answered:
    print(f"{received.psrc}\t\t{received.hwsrc}")

print(f"\nHost che non hanno risposto: {len(unanswered)}")

Come Funziona l’Esempio

  1. Creazione del pacchetto: Costruiamo un frame Ethernet broadcast contenente una ARP request per l’intera subnet
  2. Invio e ricezione: srp invia 254 ARP requests (una per ogni IP nella /24) e ascolta le risposte
  3. Timeout: Aspetta 2 secondi per raccogliere tutte le risposte
  4. Elaborazione: Itera sulle risposte ricevute e estrae IP e MAC

Parametri Avanzati di srp

srp accetta numerosi parametri opzionali:

srp(
    packet,                  # Pacchetto da inviare
    timeout=2,              # Timeout in secondi
    verbose=True,           # Mostra output dettagliato
    iface=None,            # Interfaccia di rete specifica
    inter=0,               # Intervallo tra pacchetti (secondi)
    retry=0,               # Numero di tentativi
    multi=False            # Accetta risposte multiple
)

Parametri importanti:

srp(packet, iface="eth0", timeout=2)
srp(packet, inter=0.1, timeout=2)  # 100ms tra un pacchetto e l'altro
srp(packet, retry=2, timeout=1)  # Prova 3 volte totali
srp(packet, multi=True, timeout=5)

Controllo del Verbose Output

Il parametro verbose controlla la quantità di informazioni mostrate:

# Verbose completo (default)
srp(packet, verbose=True)
# Output:
# Begin emission:
# Finished sending 1 packets.
# Received 5 packets, got 1 answers, remaining 0 packets

# Nessun output
srp(packet, verbose=0)
# Silenzioso, utile in script automatici

# Output numerico
srp(packet, verbose=2)
# Mostra anche i pacchetti individuali

Gestione delle Interfacce di Rete

Quando si lavora con srp, è importante sapere quale interfaccia di rete si sta usando:

from scapy.all import get_if_list, conf

# Elenco di tutte le interfacce disponibili
print(get_if_list())

# Interfaccia di default
print(conf.iface)

# Specificare manualmente l'interfaccia
srp(packet, iface="wlan0", timeout=2)

Esempio: Rilevamento di Host Duplicati

from scapy.all import Ether, ARP, srp

def check_duplicate_ip(target_ip):
    """
    Verifica se un IP è usato da più dispositivi (IP conflict)
    """
    # Creiamo una ARP request
    arp = ARP(pdst=target_ip)
    ether = Ether(dst="ff:ff:ff:ff:ff:ff")
    packet = ether/arp
    
    # Inviamo e riceviamo multiple risposte
    answered, _ = srp(packet, timeout=2, multi=True, verbose=0)
    
    if len(answered) > 1:
        print(f"ATTENZIONE: IP {target_ip} è usato da più dispositivi!")
        for sent, received in answered:
            print(f"  - MAC: {received.hwsrc}")
    elif len(answered) == 1:
        print(f"IP {target_ip} è usato da: {answered[0][1].hwsrc}")
    else:
        print(f"IP {target_ip} non risponde")

# Test
check_duplicate_ip("192.168.1.1")

Esempio: Network Mapping Avanzato

from scapy.all import Ether, ARP, srp
import time

def advanced_network_scan(network_range):
    """
    Scansione di rete con statistiche dettagliate
    """
    # Preparazione
    arp = ARP(pdst=network_range)
    ether = Ether(dst="ff:ff:ff:ff:ff:ff")
    packet = ether/arp
    
    # Statistiche
    start_time = time.time()
    
    # Esecuzione scan
    answered, unanswered = srp(
        packet,
        timeout=3,
        verbose=0,
        inter=0.05  # 50ms tra pacchetti per non saturare la rete
    )
    
    end_time = time.time()
    
    # Risultati
    print("=" * 60)
    print("NETWORK SCAN REPORT")
    print("=" * 60)
    print(f"Range scansionato: {network_range}")
    print(f"Tempo di esecuzione: {end_time - start_time:.2f} secondi")
    print(f"Host attivi: {len(answered)}")
    print(f"Host non raggiungibili: {len(unanswered)}")
    print("\n" + "=" * 60)
    print(f"{'IP ADDRESS':<20} {'MAC ADDRESS':<20} {'VENDOR'}")
    print("=" * 60)
    
    for sent, received in answered:
        # Puoi integrare lookup del vendor dal MAC
        print(f"{received.psrc:<20} {received.hwsrc:<20}")
    
    print("=" * 60)
    
    return answered, unanswered

# Utilizzo
results = advanced_network_scan("192.168.1.0/24")

Differenze tra srp e send

È importante distinguere tra srp() (send and receive) e sendp() (solo send):

from scapy.all import sendp, srp

# sendp() - Solo invio, nessuna ricezione
sendp(packet, iface="eth0")
# Utile per: generazione di traffico, DoS testing (etico), simulazioni

# srp() - Invio E ricezione
answered, unanswered = srp(packet, iface="eth0", timeout=2)
# Utile per: discovery, probing, testing con verifica di risposta

Best Practices con srp

  1. Timeout adeguato: Imposta un timeout ragionevole in base alla dimensione della rete

    # Rete piccola (< 50 host)
    srp(packet, timeout=1)
    
    # Rete media (50-200 host)
    srp(packet, timeout=2)
    
    # Rete grande (> 200 host)
    srp(packet, timeout=3)
    
  2. Rate limiting: Non saturare la rete con troppi pacchetti simultanei

    srp(packet, inter=0.1)  # 10 pacchetti/secondo
    
  3. Gestione errori: Sempre gestire eccezioni in ambiente di produzione

    try:
        answered, unanswered = srp(packet, timeout=2, verbose=0)
    except PermissionError:
        print("Errore: sono necessari privilegi di amministratore")
    except Exception as e:
        print(f"Errore durante l'invio: {e}")
    
  4. Interfaccia specifica: In sistemi con più interfacce, specifica quale usare

    srp(packet, iface="eth0", timeout=2)
    

5. Esempio Completo Integrato

Mettiamo insieme tutto quello che abbiamo imparato in uno script completo e commentato:

#!/usr/bin/env python3
"""
Script di Network Discovery e Analysis
Utilizza Scapy per scoprire e analizzare host in una rete locale
"""

from scapy.all import Ether, ARP, srp, hexdump
import sys

def discover_network(target_range):
    """
    Esegue una scansione ARP della rete e visualizza risultati dettagliati
    
    Args:
        target_range (str): Range IP in notazione CIDR (es. "192.168.1.0/24")
    
    Returns:
        tuple: (answered, unanswered) - risultati della scansione
    """
    
    print(f"\n{'=' * 70}")
    print(f"SCANSIONE RETE: {target_range}")
    print(f"{'=' * 70}\n")
    
    # FASE 1: Costruzione del pacchetto
    print("[FASE 1] Costruzione pacchetto ARP...")
    
    # Layer 2: Frame Ethernet (broadcast)
    ether_layer = Ether(dst="ff:ff:ff:ff:ff:ff")
    
    # Layer ARP: Request per il range specificato
    arp_layer = ARP(pdst=target_range)
    
    # Combinazione dei layer
    packet = ether_layer / arp_layer
    
    # Visualizzazione struttura pacchetto
    print("\nStruttura del pacchetto:")
    packet.show()
    
    # Visualizzazione esadecimale
    print("\nRappresentazione esadecimale:")
    hexdump(packet)
    
    # FASE 2: Invio e ricezione
    print(f"\n[FASE 2] Invio pacchetti e ascolto risposte...")
    print(f"Timeout: 3 secondi")
    print(f"Intervallo tra pacchetti: 0.1 secondi\n")
    
    try:
        answered, unanswered = srp(
            packet,
            timeout=3,
            verbose=1,
            inter=0.1
        )
    except PermissionError:
        print("\n[ERRORE] Sono richiesti privilegi di amministratore!")
        print("Esegui lo script con: sudo python3 script.py")
        sys.exit(1)
    except Exception as e:
        print(f"\n[ERRORE] Si è verificato un errore: {e}")
        sys.exit(1)
    
    # FASE 3: Analisi risultati
    print(f"\n{'=' * 70}")
    print("[FASE 3] RISULTATI SCANSIONE")
    print(f"{'=' * 70}\n")
    
    print(f"Host che hanno risposto: {len(answered)}")
    print(f"Host non raggiungibili: {len(unanswered)}")
    
    if answered:
        print(f"\n{'IP ADDRESS':<20} {'MAC ADDRESS':<20} {'STATUS'}")
        print("-" * 60)
        
        for sent, received in answered:
            print(f"{received.psrc:<20} {received.hwsrc:<20} ATTIVO")
            
            # Analisi dettagliata del primo host trovato
            if received == answered[0][1]:
                print(f"\n{'=' * 70}")
                print("ANALISI DETTAGLIATA PRIMO HOST")
                print(f"{'=' * 70}")
                print("\nPacchetto ricevuto:")
                received.show()
                print("\nDati grezzi (hexdump):")
                hexdump(received)
    else:
        print("\nNessun host ha risposto alla scansione!")
    
    return answered, unanswered


def analyze_single_host(ip_address):
    """
    Analizza un singolo host specifico
    
    Args:
        ip_address (str): Indirizzo IP dell'host da analizzare
    """
    
    print(f"\n{'=' * 70}")
    print(f"ANALISI HOST: {ip_address}")
    print(f"{'=' * 70}\n")
    
    # Costruzione pacchetto per singolo host
    packet = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(pdst=ip_address)
    
    # Invio con possibilità di risposte multiple
    answered, unanswered = srp(
        packet,
        timeout=2,
        verbose=0,
        multi=True  # Permette di rilevare IP duplicati
    )
    
    if not answered:
        print(f"L'host {ip_address} non ha risposto")
        return
    
    if len(answered) > 1:
        print(f"ATTENZIONE: Rilevati {len(answered)} dispositivi con lo stesso IP!")
        print("Possibile conflitto di indirizzi IP\n")
    
    for idx, (sent, received) in enumerate(answered, 1):
        print(f"Risposta #{idx}:")
        print(f"  IP: {received.psrc}")
        print(f"  MAC: {received.hwsrc}")
        print(f"  Tempo di risposta: {received.time - sent.sent_time:.6f}s")
        print()


def main():
    """
    Funzione principale
    """
    
    # Configurazione
    network_range = "192.168.1.0/24"  # Modifica secondo la tua rete
    
    print("""
╔═══════════════════════════════════════════════════════════════╗
║          SCAPY NETWORK DISCOVERY & ANALYSIS TOOL              ║
║                                                               ║
║  Questo script dimostra l'uso di:                            ║
║  - Ether: Costruzione frame Ethernet                         ║
║  - ARP: Address Resolution Protocol                          ║
║  - srp: Send and Receive Packets (Layer 2)                   ║
║  - hexdump: Visualizzazione esadecimale                      ║
╚═══════════════════════════════════════════════════════════════╝
    """)
    
    # Esegui scansione completa
    answered, unanswered = discover_network(network_range)
    
    # Se ci sono host attivi, analizza il primo in dettaglio
    if answered:
        first_host_ip = answered[0][1].psrc
        input(f"\nPremi INVIO per analizzare in dettaglio l'host {first_host_ip}...")
        analyze_single_host(first_host_ip)
    
    print(f"\n{'=' * 70}")
    print("SCANSIONE COMPLETATA")
    print(f"{'=' * 70}\n")


if __name__ == "__main__":
    main()

6. Considerazioni Etiche e Legali

Uso Responsabile di Scapy

Scapy è uno strumento estremamente potente che permette di manipolare il traffico di rete a basso livello. Con questo potere viene la responsabilità di usarlo eticamente e legalmente.

Quando è Legale Usare Scapy

Usi legittimi:

Quando è Illegale Usare Scapy

Usi illegali:

Raccomandazioni per Studenti e Professionisti

  1. Ambiente di test isolato: Usa sempre laboratori virtuali (VirtualBox, VMware, GNS3) per sperimentare
  2. Autorizzazione scritta: Ottieni sempre permesso scritto prima di testare reti aziendali
  3. Documentazione: Mantieni traccia di tutte le attività di testing
  4. Scope limitato: Rimani entro i confini concordati durante penetration test
  5. Responsabilità disclosure: Segnala le vulnerabilità trovate in modo responsabile

7. Esercizi Pratici

Esercizio 1: Basic ARP Discovery

Obiettivo: Scoprire tutti i dispositivi nella tua rete locale

# Completa il codice
from scapy.all import Ether, ARP, srp

# TODO: Definisci il range IP della tua rete
target = "___.___.___.___ /___"

# TODO: Costruisci il pacchetto ARP
packet = ___

# TODO: Invia e ricevi
answered, unanswered = ___

# TODO: Stampa i risultati
for ___, ___ in answered:
    print(f"IP: ___ - MAC: ___")

Esercizio 2: Analisi Hexdump

Obiettivo: Creare un pacchetto e analizzare la sua struttura esadecimale

from scapy.all import Ether, ARP, hexdump

# TODO: Crea un pacchetto ARP request per 192.168.1.1
packet = ___

# TODO: Visualizza il pacchetto in formato strutturato
___

# TODO: Visualizza il pacchetto in formato esadecimale
___

# TODO: Identifica manualmente nel hexdump:
# - Dove inizia l'indirizzo MAC di destinazione?
# - Dove si trova il campo 'operation' di ARP?
# - Dove è codificato l'indirizzo IP 192.168.1.1?

Esercizio 3: Rilevamento Host Duplicati

Obiettivo: Implementare un sistema di rilevamento conflitti IP

from scapy.all import Ether, ARP, srp

def find_duplicate_ips(network_range):
    """
    TODO: Implementa una funzione che:
    1. Scansiona il network_range
    2. Identifica IP usati da più dispositivi
    3. Stampa un report dei conflitti trovati
    """
    pass

# Test
find_duplicate_ips("192.168.1.0/24")

Esercizio 4: Network Mapper con Statistiche

Obiettivo: Creare uno script che genera statistiche di rete

from scapy.all import Ether, ARP, srp
import time

def network_statistics(network_range):
    """
    TODO: Implementa uno script che calcola:
    - Tempo medio di risposta degli host
    - Percentuale di host attivi vs totali possibili
    - Lista vendor dei MAC address (ricerca online)
    - Grafico della distribuzione IP
    """
    pass

8. Troubleshooting e FAQ

Problema: “Operation not permitted”

Sintomo: Errore quando si cerca di inviare pacchetti

Soluzione:

# Linux/Mac
sudo python3 script.py

# Windows
# Esegui il terminale come amministratore

Problema: “No module named ‘scapy’”

Sintomo: Python non trova il modulo Scapy

Soluzione:

pip install scapy
# oppure
pip3 install scapy

Problema: Timeout sempre vuoto (nessun host trovato)

Possibili cause:

  1. Firewall sta bloccando i pacchetti ARP
  2. Interfaccia di rete sbagliata
  3. Range IP errato

Soluzione:

from scapy.all import get_if_list, conf

# Verifica interfacce disponibili
print(get_if_list())

# Specifica l'interfaccia corretta
srp(packet, iface="eth0", timeout=3)

Problema: “Sniffing on … but no packet received”

Causa: Interfaccia di rete in modalità promiscua non supportata o virtualizata

Soluzione: Verifica le impostazioni della scheda di rete virtuale se stai usando VM


9. Risorse Aggiuntive

Documentazione Ufficiale

Libri Consigliati

Tutorial Online

Strumenti Complementari


10. Conclusioni

In questa guida abbiamo esplorato in profondità quattro componenti fondamentali di Scapy:

  1. Ether: La base per lavorare con frame Ethernet e comunicazioni di livello 2
  2. ARP: Il protocollo essenziale per la risoluzione IP-MAC nelle reti locali
  3. hexdump: Lo strumento per visualizzare e comprendere i pacchetti a livello binario
  4. srp: La funzione che rende possibile l’interazione bidirezionale con la rete

Padroneggiare questi concetti ti permette di:

Scapy è uno strumento che cresce con te: più lo usi, più scopri le sue potenzialità. Inizia con esempi semplici, sperimenta in ambienti sicuri, e gradualmente costruisci script sempre più complessi.

Ricorda: con grande potere viene grande responsabilità. Usa sempre Scapy in modo etico e legale.


Appendice A: Quick Reference

Import Base

from scapy.all import Ether, ARP, hexdump, srp

Creare Frame Ethernet

# Broadcast
ether = Ether(dst="ff:ff:ff:ff:ff:ff")

# Unicast
ether = Ether(dst="aa:bb:cc:dd:ee:ff", src="11:22:33:44:55:66")

Creare Pacchetti ARP

# ARP Request
arp = ARP(pdst="192.168.1.1")

# ARP Reply
arp = ARP(op=2, hwsrc="aa:bb:cc:dd:ee:ff", psrc="192.168.1.100")

Inviare e Ricevere

# Send and Receive (Layer 2)
answered, unanswered = srp(packet, timeout=2, verbose=0)

Visualizzare Pacchetti

# Vista strutturata
packet.show()

# Vista esadecimale
hexdump(packet)

Iterare sui Risultati

for sent, received in answered:
    print(f"IP: {received.psrc}, MAC: {received.hwsrc}")

Versione: 1.0
Ultima modifica: Gennaio 2025
Autore: Guida didattica per studenti ITI e Universitari
Licenza: Materiale educativo - Uso libero per scopi didattici